package com.gullmall.product.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.gullmall.product.service.CategoryBrandRelationService;
import com.gullmall.product.utils.RedisUtils;
import org.apache.commons.lang.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gullmall.common.utils.PageUtils;
import com.gullmall.common.utils.Query;

import com.gullmall.product.dao.CategoryDao;
import com.gullmall.product.entity.CategoryEntity;
import com.gullmall.product.service.CategoryService;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;


@Service("categoryService")
@Transactional
public class CategoryServiceImpl extends ServiceImpl<CategoryDao, CategoryEntity> implements CategoryService {

    @Resource(name = "stringRedisTemplate")
    private StringRedisTemplate stringRedisTemplate;
    @Resource(name = "categoryBrandRelationService")
    private CategoryBrandRelationService categoryBrandRelationService;

    @Resource(name = "redisson")
    private RedissonClient redissonClient;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<CategoryEntity> page = this.page(
                new Query<CategoryEntity>().getPage(params),
                new QueryWrapper<CategoryEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 查出所有分类和子分类，以树形结构显示
     */
    @Override
    public List<CategoryEntity> listWithTree() {
        RLock lock = null;
        List<CategoryEntity> categoryEntities = null;
        try {
            //先看redis中有没有
            String catalogTree = stringRedisTemplate.opsForValue().get("product::category-tree");

            if (!StringUtils.isEmpty(catalogTree)) {
                return JSON.parseObject(catalogTree, new TypeReference<List<CategoryEntity>>() {
                });
            }
            //分布式锁
            lock = redissonClient.getLock("catalog-tree-lock");
            lock.lock();//开启读锁

            //上锁后 再次确认redis中有没有
            String catalogTreeConfirm = stringRedisTemplate.opsForValue().get("product::category-tree");

            if (!StringUtils.isEmpty(catalogTreeConfirm)) {
                return JSON.parseObject(catalogTreeConfirm, new TypeReference<List<CategoryEntity>>() {
                });
            }
            System.out.println("查询数据库....");
            //1,查出所有
            List<CategoryEntity> categoryList = this.baseMapper.selectList(null);

            //2,组装为父子结构
            //2.1,找出所有1级分类
            categoryEntities = categoryList.stream()
                    .filter(categoryEntity -> categoryEntity.getParentCid() == 0)
                    .map((menu) -> {
                        menu.setChildren(getChildren(menu, categoryList));
                        return menu;
                    })
                    .sorted((menu1, menu2) -> (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort()))
                    .collect(Collectors.toList());
            //放入redis,1天过期
            stringRedisTemplate.opsForValue().set("product::category-tree", JSON.toJSONString(categoryEntities), 1, TimeUnit.DAYS);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (lock != null) {
                lock.unlock();
            }
        }
        return categoryEntities;
    }


    //2.2过滤子目录
    private List<CategoryEntity> getChildren(CategoryEntity root, List<CategoryEntity> all) {

        List<CategoryEntity> children = all.stream().filter(m -> Objects.equals(m.getParentCid(), root.getCatId()))
                .map((m) -> {
                    m.setChildren(getChildren(m, all));  //递归调用方法
                    return m;
                })
                .sorted((menu1, menu2) -> (menu1.getSort() == null ? 0 : menu1.getSort()) - (menu2.getSort() == null ? 0 : menu2.getSort()))
                .collect(Collectors.toList());
        return children;
    }

    //批量删除类别,返回删除的个数
    @Override
    public int removeCategoryByIds(List<Long> asList) {
        //TODO 检查当前删除的类别,是否在别的地方被引用

        return baseMapper.deleteBatchIds(asList);
    }


    @Override
    public Long[] findCatalogPath(Long catalogId) {
        List<Long> path = new ArrayList<>();
        List<Long> parentCatalogPath = findParentCatalogPath(catalogId, path);
        Collections.reverse(parentCatalogPath);//颠倒顺序
        return parentCatalogPath.toArray(new Long[parentCatalogPath.size()]);
    }


    private List<Long> findParentCatalogPath(Long catalogId, List<Long> path) {
        path.add(catalogId);
        CategoryEntity categoryEntity = this.getById(catalogId);
        if (categoryEntity.getParentCid() != 0) {
            findParentCatalogPath(categoryEntity.getParentCid(), path);
        }
        return path;
    }


    @Override
    public Boolean updateDetail(CategoryEntity category) {
        this.updateById(category);
        if (!StringUtils.isEmpty(category.getName())) {
            //如果修改了类名的名字，则其他冗余表也要修改
            categoryBrandRelationService.updateCategory(category.getCatId(), category.getName());
            //TODO 更新其他关联
        }
        return null;
    }

    //获取1级分类
    @Override
    public List<CategoryEntity> getTopLevelCategory() {
        return this.list(new QueryWrapper<CategoryEntity>()
                .eq("parent_cid", 0));
    }
}
